package net.minecraft.util;

import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.UnmodifiableIterator;

import java.lang.reflect.Array;
import java.util.*;

public class Cartesian {
    // private static final String __OBFID = "CL_00002327";

    /**
     * Create the cartesian product. This method returns an Iterable of arrays of type clazz.
     *
     * @param sets the Sets to combine. This is an Iterable of Iterables of type clazz
     */
    public static Iterable cartesianProduct(Class clazz, Iterable sets) {
        return new Cartesian.Product(clazz, (Iterable[]) toArray(Iterable.class, sets), null);
    }

    /**
     * Like cartesianProduct(Class, Iterable) but returns an Iterable of Lists instead.
     */
    public static Iterable cartesianProduct(Iterable sets) {
        return arraysAsLists(cartesianProduct(Object.class, sets));
    }

    /**
     * Convert an Iterable of Arrays (Object[]) to an Iterable of Lists
     */
    private static Iterable arraysAsLists(Iterable arrays) {
        return Iterables.transform(arrays, new Cartesian.GetList(null));
    }

    /**
     * Create a new Array of type clazz with the contents of the given Iterable
     */
    private static Object[] toArray(Class clazz, Iterable it) {
        ArrayList var2 = Lists.newArrayList();
        Iterator var3 = it.iterator();

        while (var3.hasNext()) {
            Object var4 = var3.next();
            var2.add(var4);
        }

        return (Object[]) var2.toArray(createArray(clazz, var2.size()));
    }

    private static Object[] createArray(Class p_179319_0_, int p_179319_1_) {
        return (Object[]) ((Object[]) Array.newInstance(p_179319_0_, p_179319_1_));
    }

    static class GetList implements Function {
        // private static final String __OBFID = "CL_00002325";

        private GetList() {
        }

        public List apply(Object[] array) {
            return Arrays.asList((Object[]) array);
        }

        public Object apply(Object p_apply_1_) {
            return this.apply((Object[]) p_apply_1_);
        }

        GetList(Object p_i46022_1_) {
            this();
        }
    }

    static class Product implements Iterable {
        private final Class clazz;
        private final Iterable[] iterables;
        // private static final String __OBFID = "CL_00002324";

        private Product(Class clazz, Iterable[] iterables) {
            this.clazz = clazz;
            this.iterables = iterables;
        }

        public Iterator iterator() {
            return (Iterator) (this.iterables.length <= 0 ? Collections.singletonList((Object[]) Cartesian.createArray(this.clazz, 0)).iterator() : new Cartesian.Product.ProductIterator(this.clazz, this.iterables, null));
        }

        Product(Class p_i46021_1_, Iterable[] p_i46021_2_, Object p_i46021_3_) {
            this(p_i46021_1_, p_i46021_2_);
        }

        static class ProductIterator extends UnmodifiableIterator {
            private int index;
            private final Iterable[] iterables;
            private final Iterator[] iterators;
            private final Object[] results;
            // private static final String __OBFID = "CL_00002323";

            private ProductIterator(Class clazz, Iterable[] iterables) {
                this.index = -2;
                this.iterables = iterables;
                this.iterators = (Iterator[]) Cartesian.createArray(Iterator.class, this.iterables.length);

                for (int var3 = 0; var3 < this.iterables.length; ++var3) {
                    this.iterators[var3] = iterables[var3].iterator();
                }

                this.results = Cartesian.createArray(clazz, this.iterators.length);
            }

            private void endOfData() {
                this.index = -1;
                Arrays.fill(this.iterators, (Object) null);
                Arrays.fill(this.results, (Object) null);
            }

            public boolean hasNext() {
                if (this.index == -2) {
                    this.index = 0;
                    Iterator[] var5 = this.iterators;
                    int var2 = var5.length;

                    for (int var3 = 0; var3 < var2; ++var3) {
                        Iterator var4 = var5[var3];

                        if (!var4.hasNext()) {
                            this.endOfData();
                            break;
                        }
                    }

                    return true;
                } else {
                    if (this.index >= this.iterators.length) {
                        for (this.index = this.iterators.length - 1; this.index >= 0; --this.index) {
                            Iterator var1 = this.iterators[this.index];

                            if (var1.hasNext()) {
                                break;
                            }

                            if (this.index == 0) {
                                this.endOfData();
                                break;
                            }

                            var1 = this.iterables[this.index].iterator();
                            this.iterators[this.index] = var1;

                            if (!var1.hasNext()) {
                                this.endOfData();
                                break;
                            }
                        }
                    }

                    return this.index >= 0;
                }
            }

            public Object[] next0() {
                if (!this.hasNext()) {
                    throw new NoSuchElementException();
                } else {
                    while (this.index < this.iterators.length) {
                        this.results[this.index] = this.iterators[this.index].next();
                        ++this.index;
                    }

                    return (Object[]) this.results.clone();
                }
            }

            public Object next() {
                return this.next0();
            }

            ProductIterator(Class p_i46019_1_, Iterable[] p_i46019_2_, Object p_i46019_3_) {
                this(p_i46019_1_, p_i46019_2_);
            }
        }
    }
}
